iT邦幫忙

2024 iThome 鐵人賽

DAY 20
0
Mobile Development

Flutter基礎入門系列 第 20

【Day 20】讓程式介面在新增項目時同步更新吧!

  • 分享至 

  • xImage
  •  

目前,在這個Schedrag應用程式中,雖然已經能夠成功新增項目,但程式畫面上仍然顯示沒有任何todo,這是為什麼呢?
如果回去看第一個製作的應用程式,可以看到程式碼中有著兩樣東西:ChangeNotifiernotifyListeners(),他們便是能夠讓程式隨著資料內容更新一併更新應用程式的畫面內容的背後功臣。
https://ithelp.ithome.com.tw/upload/images/20241004/20169446NESs6CdPZn.png
今日,筆者將與讀者一同學習ChangeNotifier的使用方式!學習資源為此官方說明
廢話不多說,直接開始~


應用程式的狀態管理

What is a state? 什麼是狀態?

The state of an app is everything that exists in memory when the app is running.
應用程式的狀態是執行時所有存在在記憶體中的內容。

在說明ChangeNotifier之前,先來了解Flutter是如何顯示UI吧。

Flutter是declarative(宣告、陳述性的)

大家有沒有想過Flutter是如何建立使用者介面呢?這個問題,可以用一個很簡單的公式說明:
Flutter's UI building
使用者介面將會反應程式現在的狀態state,也就是說,每當應用程式狀態改變(如:旋轉螢幕角度、新增資料),會使得使用者介面也跟著改變。而這類型的UI程式設計,可以稱為Declarative Style,程式設計師所需要做的就是說明在各個狀態中UI所應該要有的長相

Flutter的這種程式結構與其他應用程式開發工具(e.g. Android SDK, iOS UIKit)的概念不太相同,在Flutter中程式運作時會將使用者介面整個打掉重作,因為Flutter的運算速度足夠快速,甚至可以讓每一幀都如此,因此我們也不必限制自己只能修改部份UI內容。

Ephemeral State v.s. App State

Flutter中的狀態可以被分為兩大類:Ephemeral(短暫的) StateApp State。下面將來簡單介紹一下他們是什麼。
Ephermeral State and App State

  1. Ephemeral State (a.k.a. UI State / Local State)

    • 指能夠放在單一一個widget中的state,通常一旦widget停止運作,state也會跟著消失。
    • 舉例:頁面選單中現在選擇的頁面項目、動畫中現在進行的內容
      在Flutter中,這可以很簡單的利用StatefulWidget這個類別實作。
  2. App State (a.k.a. Shared State)

    • 與前者不同,有些狀態我們會希望在程式中各個部份都能夠共同使用,並且想在程式執行時一直保持著state的存在。
    • 舉例:偏好設定、網路購物的購物車、社交媒體的程式通知、信箱信件的閱讀狀態
      在App State的實作方面,沒有一個既定的實作方式,該使用什麼方式製作,會根據應用程式的用途、複雜度、團隊的習慣與經驗而有所不同。在此系列文章中,將會選擇ChangeNotifier來實作。

App State基礎實作:ChangeNotifier

ChangeNotifier是Flutter SDK的一個基礎類別,它能夠在狀態變動時給「聽眾」傳遞「通知」,也就是說,當一個物件類別是ChangeNotifier,其他物件便能夠「訂閱subscribe」它,成為聽眾並獲取通知訊息。

以之前第一個程式為範例:
我們可以看到,MyAppState是ChangeNotifier的子類別,每當使用者得到新生成的詞組,也就是getNext()被呼叫時,MyAppState()將會利用notifyListenters()來通知其他widget,將詞組存入清單的toggleFavorite()也是如此。

class MyAppState extends ChangeNotifier {
  var current = WordPair.random();
  void getNext() {
    current = WordPair.random();
    notifyListeners();
  }

  var favorites = <WordPair>[];
  void toggleFavorite() {
    if (favorites.contains(current)) {
      favorites.remove(current);
    } else {
      favorites.add(current);
    }

    notifyListeners();
  }
}

ChangeNotifierProvider

想要讓ChangeNotifier能夠派上用場,它必須要有個聽眾,那麼,要如何讓其他Widget去「訂閱」ChangeNotifier成為聽眾呢?這時候我們就需要來了解ChangeNotifierProvider

https://ithelp.ithome.com.tw/upload/images/20241004/20169446L3VcFudwRI.png
上圖來自於StackOverflow - Why do we need ChangeNotifierProvider instead of just using a ChangeNotifier?回答

ChangeNotifierProvider是一個將「訂閱ChangeNotifier」的步驟包裝成一個完整的ProviderListener協定的一個widget,它能夠知道該如何去「訂閱」、知道如何獲取「通知」,也能夠在需要的時候取消訂閱。
備註:一個ChangeNotifier需要先讓所有聽眾取消訂閱,才能正確的停止運作

現在回去看第一個程式的範例:
範例中,原本的widget MaterialApp被包在ChangeNotifierProvider之中,並且訂閱MyAppState
而在create那行,ChangeNotifierProvider是建立一個MyAppState的instance,並不會另外重新建立一個新的物件(除非有必要性)。

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => MyAppState(),
      child: MaterialApp(
        title: 'Namer App',
        theme: ThemeData(
          useMaterial3: true,
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        ),
        home: HomePage(),
      ),
    );
  }
}

如果想要使用多個provider,可以使用MultiProvider

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (context) => CartModel()),
        Provider(create: (context) => SomeOtherClass()),
      ],
      child: const MyApp(),
    ),
  );
}

至於要如何在獲得通知時更新資料內容,除了之前使用的content.watch(),還有一個名為Consumer的widget,而這部份將留到明天的文章再來說明。


謝謝閱讀到這裡的讀者,明天除了介紹Consumer,也會開始將ChangeNotifier實作在Schedrag上!
如果有任何問題或想說的,都歡迎留言及email。明天會繼續加油更新的!
email: nnyjan02426@gmail.com
github | Schedrag


上一篇
【Day 19】利用path_provider開啟資料夾與檔案
下一篇
【Day 21】獲得狀態變更通知並更新UI吧!
系列文
Flutter基礎入門30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言